#include <blkid/blkid.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include "cf_common.h"
#include "cf_blkid.h"
#include "cf_string.h"
#include "cf_json.h"
#include "lcf_log.h"
/*
{
    "disks":[
        {
            "name":"sda",
            "vendor":"",
            "size":1000000000000,
            "partitions":[
                {
                    "name":"sda1",
                    "type":"",   #primary, extended, logical
                    "size":""
                }
            ]
        }
    ]
}
*/

int CFDiskGetVendor(const char* devName, CFString* str)
{
    CFStringSafeCp(str, "/sys/block/");
    CFStringSafeCat(str, devName);
    CFStringSafeCat(str, "/device/vendor");
    LCF_DBG("%s\n", CFStringGetStr(str));
    return CFFileToString(str, CFStringGetStr(str));
}

int CFDiskGetModel(const char* devName, CFString* str)
{
    CFStringSafeCp(str, "/sys/block/");
    CFStringSafeCat(str, devName);
    CFStringSafeCat(str, "/device/model");
    LCF_DBG("%s\n", CFStringGetStr(str));
    return CFFileToString(str, CFStringGetStr(str));
}

int CFPartitionInfoSetExtra(const char* devName, CFJson* info)
{
    blkid_probe pr;
    int i, nvals;
    size_t len;
    const char *name, *value;
    
    if(!(pr = blkid_new_probe_from_filename(devName))){
        LCF_ERR_OUT(ERR_OUT, "\n");
    }
    if(blkid_probe_set_superblocks_flags(pr,
        BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
        BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
        BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION)){
        LCF_ERR_OUT(ERR_FREE_PROBE, "\n");
    }
    if(blkid_probe_enable_partitions(pr, 1) ||
            blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS) ||
            blkid_probe_enable_superblocks(pr, 1) ||
            blkid_do_safeprobe(pr) ||
            (nvals = blkid_probe_numof_values(pr))<0){
        LCF_ERR_OUT(ERR_FREE_PROBE, "\n");
    }
    for(i=0; i<nvals; i++){
        if(!blkid_probe_get_value(pr, i, &name, &value, &len)){
            //value[len]='\0';
            LCF_DBG("%s=%s\n", name, value);
            if(!strcmp(name, "TYPE")){
                CFJsonObjectAddString(info, "type", value);
            }else if(!strcmp(name, "UUID")){
                CFJsonObjectAddString(info, "uuid", value);
            }
        }
    }
    blkid_free_probe(pr);
    return 0;
ERR_FREE_PROBE:
    blkid_free_probe(pr);
ERR_OUT:
    return -1;
}

CFJson* CFDiskInfoNew(const char* devName)
{
    blkid_probe pr;
    blkid_partlist pl;
    int tmpInt;
    long long tmpInt64;
    CFJson* jConf;
    CFString tmpStr;
    CFJson* partitions;
    CFJson* partConf;
    int partCount, i;
    blkid_partition partition;
    long long size;
    const char* tmpC;
    CFJson* tmpConf;
    
    if(!devName){
        LCF_ERR_OUT(ERR_OUT, "\n");
    }
    if(CFStringInit(&tmpStr, "")){
        LCF_ERR_OUT(ERR_OUT, "\n");
    }
    CFStringSafeCp(&tmpStr, "/dev/");
    CFStringSafeCat(&tmpStr, devName);
    pr = blkid_new_probe_from_filename(CFStringGetStr(&tmpStr));
    if(!pr){
        LCF_ERR_OUT(ERR_EXIT_TMP_STR, "fileName: %s\n", CFStringGetStr(&tmpStr));
    }
    // blkid_probe_set_superblocks_flags(pr,
				// BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
				// BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
				// BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
    // blkid_probe_enable_partitions(pr, TRUE);
    // blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
    // blkid_probe_enable_superblocks(pr, TRUE);
    // blkid_do_safeprobe(pr);
    if(!(jConf = CFJsonObjectNew())){
        LCF_ERR_OUT(ERR_FREE_BLKID_PROBE, "\n");
    }
    if(CFJsonObjectAddString(jConf, "name", devName)){
        LCF_ERR_OUT(ERR_PUT_JCONF, "\n");
    }
    if((size = blkid_probe_get_size(pr))>=0){
        if(CFJsonObjectAddInt64(jConf, "size", size)){
            LCF_ERR_OUT(ERR_PUT_JCONF, "\n");            
        }
    }
    if(!CFDiskGetVendor(devName, &tmpStr) && (tmpC=CFStringGetStr(&tmpStr))){
        if(CFJsonObjectAddString(jConf, "vendor", tmpC)){
            LCF_ERR_OUT(ERR_PUT_JCONF, "\n");             
        }
    }
    if(!CFDiskGetModel(devName, &tmpStr)){
        if((tmpConf = CFJsonStringNew(CFStringGetStr(&tmpStr)))){
            if(CFJsonObjectAdd(jConf, "model", tmpConf)){
                CFJsonPut(tmpConf);
                LCF_ERR_OUT(ERR_PUT_JCONF, "\n");             
            }
        }
    }
    if((pl = blkid_probe_get_partitions(pr)) && (partCount = blkid_partlist_numof_partitions(pl))>0){
        if((partitions = CFJsonArrayNew())){
            for(i=0; i<partCount; i++){
                if((partition=blkid_partlist_get_partition(pl, i))){
                    if((partConf = CFJsonObjectNew())){
                        
                        if(blkid_partition_is_extended(partition)){
                            CFJsonObjectAddBool(partConf, "extended", TRUE);
                        }else{
                            CFJsonObjectAddBool(partConf, "extended", FALSE);
                            if((tmpC=blkid_partition_get_name(partition))){
                                CFJsonObjectAddString(partConf, "name", tmpC);
                            }
                            if((tmpInt64=blkid_partition_get_size(partition))>0){
                                CFJsonObjectAddInt64(partConf, "size", tmpInt64*512LL);
                            }
                            if((tmpInt=blkid_partition_get_partno(partition))>0){
                                char intStr[20];
                                if(!CFIntToString(intStr, tmpInt)){
                                    CFStringSafeCp(&tmpStr, devName);
                                    CFStringSafeCat(&tmpStr, intStr);
                                    CFJsonObjectAddString(partConf, "dev_name", CFStringGetStr(&tmpStr));
                                    CFStringSafeCp(&tmpStr, "/dev/");
                                    CFStringSafeCat(&tmpStr, devName);
                                    CFStringSafeCat(&tmpStr, intStr);                                   
                                    LCF_DBG("partition name: %s\n", CFStringGetStr(&tmpStr));
                                    CFPartitionInfoSetExtra(CFStringGetStr(&tmpStr),  partConf);
                                }
                            }
                            if((tmpInt=blkid_partition_get_type(partition))>0){
                                CFJsonObjectAddInt(partConf, "type_code", tmpInt);
                            }                        
                            if((tmpC=blkid_partition_get_type_string(partition))){
                                CFJsonObjectAddString(partConf, "type", tmpC);
                            }
                            if((tmpC=blkid_partition_get_uuid(partition))){
                                CFJsonObjectAddString(partConf, "uuid", tmpC);
                            }
                        }
                        if(CFJsonArrayAdd(partitions, partConf)){
                            CFJsonPut(partConf);
                        }
                    }
                }
            }
            CFJsonObjectAdd(jConf, "partitions", partitions);
        }
    }
    blkid_free_probe(pr);
    CFStringExit(&tmpStr);
    return jConf;
ERR_PUT_JCONF:
    CFJsonPut(jConf);
ERR_FREE_BLKID_PROBE:
    blkid_free_probe(pr);
ERR_EXIT_TMP_STR:
    CFStringExit(&tmpStr);
ERR_OUT:
    return NULL;
}


CFJson* CFAllDisksInfoNew()
{
    DIR* dfp;
    struct dirent* det;
    CFJson* disksConf;
    CFJson* jConf;
    
    disksConf = CFJsonArrayNew();
    if(!disksConf){
        goto ERR_OUT;
    }
    dfp = opendir("/sys/block");
    if(!dfp){
        LCF_ERR_OUT(ERR_PUT_DISKS_CONF, "open /sys/block failed\n"); 
    }
    while((det = readdir(dfp))){
        CFJson* tmpConf;
        if(!strcmp(det->d_name, ".") || !strcmp(det->d_name, "..")){
            continue;
        }
        if((tmpConf = CFDiskInfoNew(det->d_name))){
            if(CFJsonArrayAdd(disksConf, tmpConf)){
                CFJsonPut(tmpConf);
            }
        }
    }
    jConf = CFJsonObjectNew();
    if(!jConf){
         LCF_ERR_OUT(ERR_PUT_DISKS_CONF, "\n");
    }
    if(CFJsonObjectAdd(jConf, "disks", disksConf)){
        CFJsonPut(disksConf);
    }
    return jConf;
ERR_PUT_DISKS_CONF:
    CFJsonPut(disksConf);
ERR_OUT:
    return NULL;
}
